/*
 * Copyright (c) 2008, Xiong Yingfei, the University of Tokyo All rights
 * reserved.
 */
package ac.jp.u_tokyo.SyncLib.interactive;

import java.util.Stack;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import ac.jp.u_tokyo.SyncLib.Mod;
import ac.jp.u_tokyo.SyncLib.Sync;
import ac.jp.u_tokyo.SyncLib.util.Helper;

@Aspect
public class TraceAspect
{

	int indent = 0;

	Stack<Long> starts = new Stack<Long>();

	public static int _maxLevels = 2;//Integer.MAX_VALUE;

	protected static boolean _printException = false;

	public static boolean _showTrace = true;

	public static boolean _showTime = true;

	private void printIndent()
	{
		for (int i = 0; i < indent; i++)
		{
			System.out.print("  ");
		}
	}

	@Before("call(* Sync.synchronize(..)) && args(modifications) && target(sync)")
	public void printBeforeSync(Mod[] modifications, Sync sync)
	{
		if (!TraceAspect.showTrace()) return;
		if (indent < TraceAspect._maxLevels)
		{
			printIndent();
			System.out.println(sync.toString() + ".synchronize() called.");
			printIndent();
			System.out.println("  input: " + Helper.toString(modifications));
			if (_showTime)
			{
				starts.push(System.currentTimeMillis());
			}
		}
		indent++;
	}

	@Before("call(* Sync.resynchronize(..)) && args(values, modifications) && target(sync)")
	public void printBeforeResync(Object[] values, Mod[] modifications,
			Sync sync)
	{
		if (!TraceAspect.showTrace()) return;
		if (indent < TraceAspect._maxLevels)
		{
			printIndent();
			System.out.println(sync.toString() + ".resynchronize() called.");
			printIndent();
			System.out.println("  input values: " + Helper.toString(values));
			printIndent();
			System.out
					.println("  input mods:" + Helper.toString(modifications));
			if (_showTime)
			{
				starts.push(System.currentTimeMillis());
			}
		}
		indent++;
	}

	@AfterReturning(pointcut = "call(* Sync.synchronize(..)) || call(* Sync.resynchronize(..)) ", returning = "result")
	public void printAfterReturn(Mod[] result)
	{
		if (!TraceAspect.showTrace()) return;
		indent--;
		if (indent < TraceAspect._maxLevels)
		{
			printIndent();
			System.out.println("  output: " + Helper.toString(result));
			if (_showTime)
			{
				printIndent();
				System.out.println("  Time: " + (System.currentTimeMillis() - starts.pop()) + "ms.");
			}
		}
	}

	@AfterThrowing(pointcut = "call(* Sync.synchronize(..)) || call(* Sync.resynchronize(..)) ", throwing = "err")
	public void printAfterThrow(Exception err)
	{
		if (!TraceAspect.showTrace()) return;
		indent--;
		if (indent < TraceAspect._maxLevels)
		{
			printIndent();
			System.out.println("  synchronization failed.");
			if (TraceAspect._printException) err.printStackTrace(System.out);
			if (_showTime)
			{
				printIndent();
				System.out.println("  Time: "
						+ (System.currentTimeMillis() - starts.pop()) + "ms.");
			}
		}
	}

	public static boolean showTrace()
	{
		return _showTrace;
	}

}
